import java.util.ArrayList;
import java.util.Arrays;
/**
 * The BigTwo class implements the CardGame interface and is used to model a Big Two card game. It has private instance
 * variables for storing the number of players, a deck of cards, a list of players, a list of hands played on the table,
 * an index of the current player, and a user interface. Below is a detailed description for the BigTwo class.
 *
 * @author Jiang Feiyu (3035770800)
 */

public class BigTwo {
    int first = 0;
    /**
     * an int specifying the number of players.
     */
    private  int numOfPlayers;
    /**
     * a deck of cards
     */
    private Deck deck;
    /**
     * a list of players
     */
    private ArrayList<CardGamePlayer> playerList;
    /**
     * a list of hands played on the table
     */
    private ArrayList<Hand> handsOnTable;
    /**
     * an integer specifying the index of the current player
     */
    private int currentPlayerIdx;
    /**
     * a BigTwoUI object for providing the user interface
     */
    private BigTwoUI ui;

    /**
     *  a constructor for creating a Big Two card game. You should (i) create 4 players and add them to the player list;
     *  and (ii) create a BigTwoUI object for providing the user interface.
     */
    public BigTwo()
    {
        this.playerList = new ArrayList<CardGamePlayer> ();
        for(int i = 0; i < 4;i++)
        {
            CardGamePlayer player = new CardGamePlayer();
            this.playerList.add(player);
        }
        handsOnTable = new ArrayList<Hand>();
        ui = new BigTwoUI(this);
    }
    /**
     * a method for getting the number of players.
     *
     * @return Returns  number of players.
     */
    public int getNumOfPlayers()
    {
        return numOfPlayers;
    }
    /**
     * a method for retrieving the deck of cards being used.
     *
     * @return Returns a deck object containing the deck of cards currently being played
     */
    public Deck getDeck()
    {
        return deck;
    }

    /**
     *  method for retrieving the list of players.
     *
     * @return An ArrayList containing the list of players.
     */
    public ArrayList<CardGamePlayer> getPlayerList()
    {
        return playerList;
    }

    /**
     * a method for retrieving the list of hands played on the table.
     *
     * @return An ArrayList containing the cards played by the previous player.
     */
    public ArrayList<Hand> getHandsOnTable()
    {
        return handsOnTable;
    }

    /**
     * a method for retrieving the index of the current player.
     *
     * @return an Int type which could either be 0, 1, 2 or 3.
     */
    public int getCurrentPlayerIdx()
    {
        return currentPlayerIdx;
    }

    /**
     * a method for starting/restarting the game with a given shuffled deck of cards. You should
     * (i) remove all the cards from the players as well as from the table;
     * (ii) distribute the cards to the players;
     * (iii) identify the player who holds the Three of Diamonds;
     * (iv) set both the currentPlayerIdx of the BigTwo object and the activePlayer of the BigTwoUI object to the index
     * of the player who holds the Three of Diamonds;
     * (v) call the repaint() method of the BigTwoUI object to show the cards on the table; and (vi) call the
     * promptActivePlayer() method of the BigTwoUI object to prompt user to select cards and make his/her move.
     */
    void start(Deck deck){

        //clear players' hands
        for (int i=0;i<this.playerList.size();i++) {
            this.playerList.get(i).removeAllCards();
        }
        //reset current player index
        this.currentPlayerIdx=0;
        this.handsOnTable=new ArrayList<Hand>();

        //take shuffled deck
        deck.initialize();
        deck.shuffle();

        // ii) distributing the cards to the four players
        // iii) Identify the player with Three of Diamonds
        int counter = 0;
        int hold_3_diam_idx = 0;
        Card beginCard = new Card(0,2);

        int index = 0;
        while (index < 52){
            if (deck.getCard(index).rank == beginCard.rank) {
                if (deck.getCard(index).suit==beginCard.suit){
                    hold_3_diam_idx = counter % 4;
                }
            }
            this.playerList.get(counter % 4).addCard(deck.getCard(index));
            counter ++;
            index = index + 1;
        }

        this.currentPlayerIdx = hold_3_diam_idx;
        this.ui.setActivePlayer(hold_3_diam_idx);

        //Sort players’ hands for better display
        for (int i = 0; i < 4; i ++){
            playerList.get(i).getCardsInHand().sort();
        }

        //Resetting the UI,
        this.ui = new BigTwoUI(this);
        //set an active player
        this.ui.setActivePlayer(this.currentPlayerIdx);
        //displaying table and messages
        this.ui.repaint();
        this.ui.promptActivePlayer();

        while (this.endOfGame() != true) {
            //Resetting the UI
            this.ui = new BigTwoUI(this);
            this.ui.setActivePlayer(this.currentPlayerIdx);
            this.ui.promptActivePlayer();
        }

        this.ui.setActivePlayer(-1);
        System.out.println("Game ends");
        // check the winner
        for(int i = 0;i<4;i++) {
            if(this.playerList.get(i).getCardsInHand().isEmpty()) {
                System.out.println(this.playerList.get(i).getName()+" wins the game.");
                continue;
            }
            System.out.println(this.playerList.get(i).getName()+" has "+this.playerList.get(i).getNumOfCards()+" cards in hand.");
        }
    }

    /**
     * a method for making a move by a player with the specified index using the cards specified by the list of indices.
     * This method should be called from the BigTwoUI after the active player has selected cards to make his/her move.
     * You should simply call the checkMove() method with the playerIdx and cardIdx as the arguments. *
     */
    void makeMove(int playerIdx, int[] cardIdx){this.checkMove(playerIdx, cardIdx);}

    /**
     * a method for checking a move made by a player. This method should be called from the makeMove() method. *
     *
     * @return
     */
    boolean checkMove(int playerIdx, int[] cardIdx) {
        // The first card need to be sent
        Card diamond_3 = new Card(0, 2);

        CardList cardlist = new CardList();
        boolean temp_valid = false;
        System.out.print("cardIdx" + cardIdx);

        if (cardIdx != null) {
            System.out.println("a");
            cardlist = this.playerList.get(this.currentPlayerIdx).play(cardIdx);


            composeHand(this.playerList.get(this.currentPlayerIdx), cardlist);
            System.out.println("b");
            if (this.handsOnTable.isEmpty()) {
                if (composeHand(this.playerList.get(this.currentPlayerIdx), cardlist).contains(diamond_3) &&
                        composeHand(playerList.get(this.currentPlayerIdx), cardlist).isEmpty() == false &&
                        composeHand(playerList.get(this.currentPlayerIdx), cardlist).isValid() == true) {
                    System.out.println("c");
                    this.handsOnTable.add(composeHand(this.playerList.get(playerIdx), cardlist));
                    System.out.println("this.handsOnTable"+this.handsOnTable);

                    Arrays.sort(cardIdx);
                    for(int v=0;v<cardIdx.length;v++) {
                        this.playerList.get(playerIdx).getCardsInHand().removeCard(cardIdx[v]);
                        for(int j=0;j<cardIdx.length;j++) {
                            cardIdx[j]--;
                        }
                    }
                    this.currentPlayerIdx ++;
                    this.currentPlayerIdx = this.currentPlayerIdx%4;
                    temp_valid = true;

                } else {
                    System.out.println("d");
                    System.out.println("Not a legal move!");
                    temp_valid = false;
                }
            } else {
                System.out.println("e");
                System.out.println("this.handsOnTable" + this.handsOnTable);
                if (this.handsOnTable.get(this.handsOnTable.size() - 1).getPlayer() == this.playerList.get(currentPlayerIdx)) {
                    System.out.println("f");
                    this.handsOnTable.add(composeHand(this.playerList.get(playerIdx), cardlist));
                    System.out.println("this.handsOnTable" + this.handsOnTable);

                    Arrays.sort(cardIdx);
                    for (int v = 0; v < cardIdx.length; v++) {
                        this.playerList.get(playerIdx).getCardsInHand().removeCard(cardIdx[v]);
                        for (int j = 0; j < cardIdx.length; j++) {
                            cardIdx[j]--;
                        }
                    }
                    this.currentPlayerIdx++;
                    this.currentPlayerIdx = this.currentPlayerIdx % 4;
                    temp_valid = true;

                } else {
                    System.out.println("g");
                    temp_valid = composeHand(playerList.get(this.currentPlayerIdx), cardlist).beats(this.handsOnTable.get(this.handsOnTable.size() - 1));
                    if (temp_valid) {
                        System.out.println("h");
                        this.handsOnTable.add(composeHand(this.playerList.get(playerIdx), cardlist));
                        System.out.println("this.handsOnTable" + this.handsOnTable);

                        Arrays.sort(cardIdx);
                        for (int v = 0; v < cardIdx.length; v++) {
                            this.playerList.get(playerIdx).getCardsInHand().removeCard(cardIdx[v]);
                            for (int j = 0; j < cardIdx.length; j++) {
                                cardIdx[j]--;
                            }
                        }
                        this.currentPlayerIdx++;
                        this.currentPlayerIdx = this.currentPlayerIdx % 4;

                    } else {
                        System.out.println("i");
                        System.out.println("Not a legal move!!!");
                        temp_valid = false;
                    }
                }
            }
        } else {
            System.out.println("k");
            // If all other players passed, then need to play in the next turn
            System.out.println("this.handsOnTable.size()");
            System.out.println(this.handsOnTable.size());
            if ((this.handsOnTable.size() - 1) > 0){
                System.out.println(this.handsOnTable.get(this.handsOnTable.size() - 1).getPlayer());
                System.out.println(this.playerList.get(currentPlayerIdx));
                if (this.handsOnTable.get(this.handsOnTable.size() - 1).getPlayer() == this.playerList.get(currentPlayerIdx)) {
                    System.out.println("l");
                    System.out.println("Not a legal move!!");
                    temp_valid = false;
                } else {
                    System.out.println("m");
                    this.currentPlayerIdx++;
                    this.currentPlayerIdx = this.currentPlayerIdx % 4;
                    this.ui.setActivePlayer(this.currentPlayerIdx);
                    System.out.println("{Pass}");
                    temp_valid = true;
                }
            }
            else if ((this.handsOnTable.size() - 1) < 0){
                System.out.println("l");
                System.out.println("Not a legal move!!");
                temp_valid = false;
            }
            else{
                System.out.println("m");
                this.currentPlayerIdx++;
                this.currentPlayerIdx = this.currentPlayerIdx % 4;
                this.ui.setActivePlayer(this.currentPlayerIdx);
                System.out.println("{Pass}");
                temp_valid = true;
                }
            }

            if (temp_valid) {
                System.out.println("n");
                this.ui.setActivePlayer(this.currentPlayerIdx);
                this.ui.repaint();
            }

        return false;
    }
        /**
         * a method for checking if the game ends.
         */
        public boolean endOfGame() {
            for (int i = 0 ; i<4;i++) {
                if(this.playerList.get(i).getCardsInHand().isEmpty()) {
                    return true;
                }
            }
            return false;
        }
    /**
     * A method to return a valid hand from all the list of cards played by the player.
     *
     * @param player A CardGamePlayer object which contains the list of players in the game.
     * @param cards A CardList object which contains list of cards played by the active player.
     *
     * @return Hand The type of hand.
     */
    public static Hand composeHand(CardGamePlayer player, CardList cards) {
        Single single = new Single(player,cards);
        Pair pair = new Pair(player,cards);
        Triple triple = new Triple(player,cards);
        Straight straight = new Straight(player,cards);
        Flush flush = new Flush(player,cards);
        FullHouse fullhouse = new FullHouse(player,cards);
        Quad quad = new Quad(player,cards);
        StraightFlush straightflush = new StraightFlush(player,cards);

        if(straightflush.isValid())
        {
            return straightflush;
        }

        else if(quad.isValid())
        {
            return quad;
        }

        else if(fullhouse.isValid())
        {
            return fullhouse;
        }

        else if(flush.isValid())
        {
            return flush;
        }

        else if(straight.isValid())
        {
            return straight;
        }

        else if(triple.isValid())
        {
            return triple;
        }

        else if(pair.isValid())
        {
            return pair;
        }

        if(single.isValid())
        {
            return single;
        }
        return new Hand(player, cards);
    }

    public static void main(String[] args) {
        BigTwo game = new BigTwo();
        BigTwoDeck deck = new BigTwoDeck();
        deck.shuffle();
        game.start(deck);
    }
}